iT邦幫忙

2023 iThome 鐵人賽

DAY 13
0
Vue.js

淺談vue3源碼,很淺的那種系列 第 13

[Day 13] runtime-core——render方法

  • 分享至 

  • xImage
  •  

「哼,哼哼哼哼哼哼哼,哈哈,啊哈哈,啊哈哈哈哈哈哈哈哈哈!」——米哈遊

上回書說道……算了沒時間前情提要了。總之就先建個文件/src/runtime-dom/index.ts把前兩天我們整理的渲染dom跟給節點補屬性和事件的東西都拼裝到一起吧。

import { createRenderer } from "../runtime-core";
import { VNode } from "../runtime-core/vnode";
import { nodeOps } from "./nodeOps";
import { patchProp } from "./patchProp";

const renderOptions = Object.assign(nodeOps, { patchProp });
export type RenderOptions = typeof renderOptions;

export function render(vnode: VNode, container: VNode | HTMLElement) {
  createRenderer(renderOptions).render(vnode, container);
}

除了createRenderer是我們接下來要去寫的東西,其他渲染dom元素相關的模塊都已經完成,並且我們在runtime-dom/index.ts把它們拼裝到了一起,之後就不需要再動runtime-dom裡的任何文件了。

實際上真正的vue源碼的renderOptions是會視情況變動的,所以以參數的形式傳入createRenderer。但還記得咱這次鐵人賽的標題吧?不聊太深,就淺談,所以renderOptions寫死,接下來要做的createRenderer會基於寫死的renderOptions去回傳一個render方法,比對接收的虛擬dom,並將之渲染成真實dom。

所以在/src/runtime-core/路徑下建立index.ts及renderer.ts兩個文件吧。

index.ts的內容極其簡單:

export * from './vnode';
export { createRenderer } from './renderer';

renderer.ts這邊,要先暴露一個createRenderer方法,基於runtime-dom的renderOptions渲染dom:

import { RenderOptions } from "../runtime-dom";
import { Text, VNode } from "./vnode";

export const createRenderer = (renderOptions: RenderOptions) => {
  const {
    insert: hostInsert,
    remove: hostRemove,
    setElementText: hostSetElementText,
    setText: hostSetText,
    parentNode: hostParentNode,
    nextSibling: hostNextSibling,
    createElement: hostCreateElement,
    createText: hostCreateText,
    patchProp: hostPatchProp
  } = renderOptions;

  const render = (vnode: VNode, container: any) => {

  };

  return { render };
};

然後是render的部分。render接收兩個參數,分別是代表新的虛擬dom的vnode,以及container,代表要把這些vnode渲染到哪個dom元素之中。

接著我們完成render的方法體:

const render = (vnode: VNode, container: any) => {
  if (vnode == null) {
    if (container._vnode) unmount(container._vnode);
  } else {
    patch(container._vnode || null, vnode, container);
  }
  container._vnode = vnode;
};

各位是否曾在vue的專案console.dir打印dom元素時看到過_vnode這個屬性?這個_vnode就是真實dom所對應的虛擬dom,我們可以在render方法的方法體最後一行中看到container._vnode = vnode,由此可以得知透過render方法直接渲染的dom元素身上會有一個_vnode屬性,指向它在內存中的虛擬dom。

因為有將虛擬dom的地址掛載到真實dom上,我們可以從container參數取得舊的虛擬dom,然後就能分成以下四種情況:

也就是如果新的虛擬dom是null,代表要把這個container裡面的所有dom元素都刪除;如果有接收到新的虛擬dom,則比對新舊虛擬dom。

把dom元素都刪除的unmount簡單粗暴,就真的只是字面上的把dom元素都刪除:

const unmount = (vnode: VNode) => {
  hostRemove(vnode.el)
}

至於比對新舊虛擬dom的patch就比較困難了,從明天起,我們將會一步一步去實現這個比對新舊虛擬dom的方法。

githubmain分支commit「[Day 13] runtime-core——render方法」


上一篇
[Day 12] runtime-dom——封裝操作dom元素的方法 - 2
下一篇
[Day 14] runtime-core——比對新舊虛擬dom
系列文
淺談vue3源碼,很淺的那種31
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言